package com.ruibang.glass.quality.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruibang.glass.common.domain.quality.HotGlassDefectNum;
import com.ruibang.glass.quality.constant.QualityErrorCode;
import com.ruibang.glass.quality.domain.req.SchedulingReportReq;
import com.ruibang.glass.quality.domain.resp.*;
import com.ruibang.glass.quality.entity.BcStationMesDetail;
import com.ruibang.glass.quality.entity.SchedulingReport;
import com.ruibang.glass.quality.entity.SlStationMes3Detail;
import com.ruibang.glass.quality.mapper.SchedulingReportMapper;
import com.ruibang.glass.quality.service.SchedulingReportService;
import com.teaming.cloud.framework2.autoconfigure.redis.RedisSingleService;
import com.teaming.cloud.framework2.autoconfigure.redis.RedisSingleService;
import com.teaming.cloud.framework2.common.model.ResultBody;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @Author: songJinKang
 * @CreateTime: 2023-09-18  10:04
 * @Description: TODO
 * @Version: 1.0
 */

@Slf4j
@Service
public class SchedulingReportServiceImpl extends ServiceImpl<SchedulingReportMapper, SchedulingReport> implements SchedulingReportService {

    @Autowired
    private SchedulingReportMapper schedulingReportMapper;

//    @Autowired
//    private RedisSingleService redisService;

    @Autowired
    private RedisSingleService redisService;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    /**
     * @Description: 查询
     * @version v1.0
     * @author songJinKang
     * @date 2023-09-18 15:03
     */
    @Override
    public ResultBody querySchedulingReport(SchedulingReportReq schedulingReportReq) {
        QueryWrapper<SchedulingReport> qw = new QueryWrapper<>();
        qw.lambda().select(SchedulingReport::getId, SchedulingReport::getDate, SchedulingReport::getDispatch, SchedulingReport::getClasses).orderByDesc(SchedulingReport::getDate);
        Page<SchedulingReport> pageData = schedulingReportMapper.selectPage(new Page<>(schedulingReportReq.getPageNo(), schedulingReportReq.getPageSize()), qw);
//        List<SchedulingReport> records = pageData.getRecords();
//        if (CollectionUtils.isNotEmpty(records)) {
//            for (SchedulingReport record : records) {
//                record.setHotInfo(JSONObject.parseObject(record.getHot(), SchedulingHotInfo.class));
//                record.setColdInfo(JSONObject.parseObject(record.getCold(), SchedulingColdInfo.class));
//                record.setMonthProductionInfo(JSONObject.parseObject(record.getMonthProduction(), SchedulingMonthProduction.class));
//            }
//        }
        return ResultBody.success(pageData);
    }

    /**
     * @Description: 保存调度表
     * @version v1.0
     * @author songJinKang
     * @date 2023-09-18 14:06
     */
    @Override
    public ResultBody insert(SchedulingReport schedulingReport) {
        setTableValue(schedulingReport);
        this.save(schedulingReport);
        return ResultBody.success();
    }

    /**
     * @Description: 修改调度表
     * @version v1.0
     * @author songJinKang
     * @date 2023-09-18 15:00
     */
    @Override
    public ResultBody edit(SchedulingReport schedulingReport) {
        setTableValue(schedulingReport);
        this.updateById(schedulingReport);
        return ResultBody.success();
    }

    /**
     * @Description: 根据id获取详情
     * @version v1.0
     * @author songJinKang
     * @date 2023-09-20 11:18
     */
    @Override
    public ResultBody getDetail(String id) {
        if (StringUtils.isNotBlank(id)) {
            SchedulingReport schedulingReport = this.getById(id);
            if (schedulingReport != null) {
                schedulingReport.setHotInfo(JSON.parseObject(schedulingReport.getHot(), SchedulingHotInfo.class));
                schedulingReport.setColdInfo(JSON.parseObject(schedulingReport.getCold(), SchedulingColdInfo.class));
                schedulingReport.setMonthProductionInfo(JSON.parseObject(schedulingReport.getMonthProduction(), SchedulingMonthProduction.class));
                schedulingReport.setProductionDetail(JSON.parseObject(schedulingReport.getProduction(), ProductionDetail.class));
                schedulingReport.setQualityDetail(JSON.parseObject(schedulingReport.getQuality(), QualityDetail.class));
                return ResultBody.success(schedulingReport);
            }
        }
        return ResultBody.success();
    }


    private void setTableValue(SchedulingReport schedulingReport) {
        //热端信息
        SchedulingHotInfo hotInfo = schedulingReport.getHotInfo();
        if (hotInfo != null) {
            schedulingReport.setHot(JSON.toJSONString(hotInfo));
        }
        //冷端信息
        SchedulingColdInfo coldInfo = schedulingReport.getColdInfo();
        if (coldInfo != null) {
            schedulingReport.setCold(JSON.toJSONString(coldInfo));
        }
        //月综合信息
        SchedulingMonthProduction monthProductionInfo = schedulingReport.getMonthProductionInfo();
        if (monthProductionInfo != null) {
            schedulingReport.setMonthProduction(JSON.toJSONString(monthProductionInfo));
        }
        //质量信息
        QualityDetail qualityDetail = schedulingReport.getQualityDetail();
        if (qualityDetail != null) {
            schedulingReport.setQuality(JSON.toJSONString(qualityDetail));
        }
        //生产信息
        ProductionDetail productionDetail = schedulingReport.getProductionDetail();
        if (productionDetail != null) {
            schedulingReport.setProduction(JSON.toJSONString(productionDetail));
        }
    }

    @PostConstruct
    public void initGlassData() {
        CompletableFuture.runAsync(() -> {
            for (int i = 1; i <= 8; i++) {
                LocalDate date = LocalDate.now().minusDays(i);
                SchedulingReportReq schedulingReportReq = new SchedulingReportReq();
                schedulingReportReq.setSchedulingDate(date);
                getGlassData(schedulingReportReq);
            }
            log.error("调用初始化完成");
        },threadPoolTaskExecutor);
    }


    /**
     * @Description: 缓存当天数据
     * @version v1.0
     * @author songJinKang
     * @date 2023-12-06 16:05
     */
    @Scheduled(cron = "0 20 7 * * ?")
    public void defectTask() {
        SchedulingReportReq schedulingReportReq = new SchedulingReportReq();
        schedulingReportReq.setSchedulingDate(LocalDate.now().plusDays(1));
        getGlassData(schedulingReportReq);
    }


    /**
     * @Description: 获取热端玻璃数据
     * @version v1.0
     * @author songJinKang
     * @date 2023-12-06 10:56
     */
    private SchedulingHotInfo getHotGlassData(LocalDateTime startTime, LocalDateTime endTime) {
        //转毫秒时间撮
        long end = endTime.toInstant(ZoneOffset.of("+8")).toEpochMilli();
        long start = startTime.toInstant(ZoneOffset.of("+8")).toEpochMilli();
        SchedulingHotInfo hotGlassData = baseMapper.getHotGlassData(start, end);
        //统计气泡、结石数
        HotGlassDefectNum hotGlassDefectNum = baseMapper.getHotGlassDefectNum(startTime, endTime);
        if (hotGlassDefectNum != null) {
            hotGlassData.setA28(String.valueOf(hotGlassDefectNum.getBubbleNum()));
            hotGlassData.setA31(String.valueOf(hotGlassDefectNum.getStoneNum()));
        }
        return hotGlassData;
    }

    /**
     * @Description: 根据日期获取玻璃生产数据
     * @version v1.0
     * @author songJinKang
     * @date 2023-11-08 9:45
     */
    @Override
    public ResultBody getGlassData(SchedulingReportReq schedulingReportReq) {
        LocalDate date = schedulingReportReq.getSchedulingDate();
        if (date == null) {
            return ResultBody.fail(QualityErrorCode.INVALID_PARAM.getCode(), QualityErrorCode.INVALID_PARAM.getMsg());
        }
        //大于当前时间直接返回
        if (date.compareTo(LocalDate.now()) == 0 || date.compareTo(LocalDate.now()) > 0) {
            return ResultBody.success();
        }

        Serializable serializable = redisService.get(date.toString());

        if (serializable != null && StringUtils.isNotBlank(serializable.toString())) {
            SchedulingReport schedulingReport = JSONObject.parseObject(serializable.toString(), SchedulingReport.class);
            return ResultBody.success(schedulingReport);
        } else {
            LocalDateTime startTime = LocalDateTime.of(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), 7, 0, 0);
            LocalDateTime endTime = startTime.plusDays(1);
            log.error("调度  dayTime: {}----{}", startTime, endTime);

            long start;
            long end;
            end = endTime.toInstant(ZoneOffset.of("+8")).toEpochMilli();
            start = startTime.toInstant(ZoneOffset.of("+8")).toEpochMilli();


            List<CompletableFuture<Void>> taskList = new ArrayList<>();

            SchedulingReport schedulingReport = new SchedulingReport();

            //冷端
            CompletableFuture<Void> coldTask = CompletableFuture.runAsync(() -> {
                List<SlStationMes3Detail> slStationMes3ColdDetails = baseMapper.getColdGlassData(String.valueOf(start), String.valueOf(end));
                SchedulingColdInfo coldInfo = processColdMes3(slStationMes3ColdDetails);
                schedulingReport.setColdInfo(coldInfo);
            },threadPoolTaskExecutor);

            //热端
            CompletableFuture<Void> hotTask = CompletableFuture.runAsync(() -> {
                SchedulingHotInfo hotInfo = getHotGlassData(startTime, endTime);
                schedulingReport.setHotInfo(hotInfo);
            },threadPoolTaskExecutor);

            //月累计
            SchedulingMonthProduction monthProductionInfo = new SchedulingMonthProduction();

            LocalDateTime monthEndTime = LocalDateTime.of(endTime.getYear(), endTime.getMonthValue(), endTime.getDayOfMonth(), 7, 0, 0);
            LocalDateTime monthStartTime;
            if (date.compareTo(LocalDate.now().with(TemporalAdjusters.lastDayOfMonth())) == 0) {
                monthStartTime = LocalDateTime.of(date.getYear(), date.getMonthValue(), 1, 7, 0, 0);
            } else {
                monthStartTime = LocalDateTime.of(startTime.getYear(), startTime.getMonthValue(), 1, 7, 0, 0);
            }
            log.error("调度 monthTime: {}---{}", monthStartTime, monthEndTime);
            long monthStart;
            long monthEnd;
            monthEnd = monthEndTime.toInstant(ZoneOffset.of("+8")).toEpochMilli();
            monthStart = monthStartTime.toInstant(ZoneOffset.of("+8")).toEpochMilli();

            //月冷端
            CompletableFuture<Void> monthColdTask = CompletableFuture.runAsync(() -> {
                List<SlStationMes3Detail> monthColdInfo = baseMapper.getMonthColdInfo(String.valueOf(monthStart), String.valueOf(monthEnd));
                processColdMonthlyCumulative(monthColdInfo, monthProductionInfo);
            },threadPoolTaskExecutor);
            //月热端
            CompletableFuture<Void> monthHotTask = CompletableFuture.runAsync(() -> {
                getHotMonthGlassData(monthProductionInfo, monthStartTime, monthEndTime);
            },threadPoolTaskExecutor);
            //库存
            CompletableFuture<Void> cpkcTask = CompletableFuture.runAsync(() -> {
                //成品良品库存数
                int cpkcNum = baseMapper.getMoMcpkcData();
                monthProductionInfo.setC37(String.valueOf(cpkcNum));
            },threadPoolTaskExecutor);


            taskList.add(coldTask);
            taskList.add(hotTask);
            taskList.add(monthColdTask);
            taskList.add(monthHotTask);
            taskList.add(cpkcTask);

            //set月累计数据
            schedulingReport.setMonthProductionInfo(monthProductionInfo);
            //等待
            for (CompletableFuture<Void> completableFuture : taskList) {
                completableFuture.join();
            }
            if (schedulingReport.getHotInfo() != null && schedulingReport.getColdInfo() != null) {
                redisService.set(date.toString(), JSONObject.toJSONString(schedulingReport), 30 * 24 * 60 * 60);
            }
            return ResultBody.success(schedulingReport);
        }
    }

    private void getHotMonthGlassData(SchedulingMonthProduction schedulingMonthProduction, LocalDateTime monthStartTime, LocalDateTime monthEndTime) {
        long monthEndTs = monthEndTime.toInstant(ZoneOffset.of("+8")).toEpochMilli();
        long monthStartTs = monthStartTime.toInstant(ZoneOffset.of("+8")).toEpochMilli();
        MomMonthHotData momMonthHotData = baseMapper.getHotMonthGlassData(monthStartTs, monthEndTs);
        if (momMonthHotData != null) {
            schedulingMonthProduction.setC7(momMonthHotData.getGh());
            schedulingMonthProduction.setC4(momMonthHotData.getBcpLp());
            schedulingMonthProduction.setC1(momMonthHotData.getHq());
            //统计品质数
            HotGlassDefectNum hotGlassDefectNum = baseMapper.getHotGlassDefectNum(monthStartTime, monthEndTime);
            if (hotGlassDefectNum != null) {
                int pzNum = hotGlassDefectNum.getBubbleNum() + hotGlassDefectNum.getStoneNum();
                pzNum = pzNum + Integer.parseInt(momMonthHotData.getPz());
                schedulingMonthProduction.setC10(String.valueOf(pzNum));
            }
        }
    }

    private void processHotMonthlyCumulative(List<BcStationMesDetail> monthHotInfo, SchedulingMonthProduction monthProductionInfo) {
        if (CollectionUtils.isNotEmpty(monthHotInfo)) {
            //取小时中最大数据
            List<BcStationMesDetail> glassHotData = getHotMaxTime(monthHotInfo);
            //统计
            int hq = 0;//总横切数
            for (BcStationMesDetail detail : glassHotData) {
                hq += detail.getRealityHq();
            }
            monthProductionInfo.setC1(String.valueOf(hq));
        }
    }

    private List<BcStationMesDetail> getHotMaxTime(List<BcStationMesDetail> monthHotInfo) {
        List<BcStationMesDetail> glassHotData = new ArrayList<>();
        Map<String, List<BcStationMesDetail>> slStationMes3ColdDetailMap = monthHotInfo.stream().filter(e -> StringUtils.isNotBlank(e.getCreateTime()))
                .collect(Collectors.groupingBy(e -> e.getCreateTime().substring(0, 13)));
        slStationMes3ColdDetailMap.forEach((k, v) -> {
            Optional<BcStationMesDetail> max = v.stream().max(Comparator.comparing(BcStationMesDetail::getCreateTime));
            if (max.isPresent()) {
                BcStationMesDetail slStationMes3Detail = max.get();
                glassHotData.add(slStationMes3Detail);
//                log.error("热端时间最大的记录是：{}", slStationMes3Detail.getCreateTime());
            } else {
                log.error("List为空，没有最大的记录");
            }
        });
        return glassHotData;
    }

    private void processColdMonthlyCumulative(List<SlStationMes3Detail> monthColdInfo, SchedulingMonthProduction monthProductionInfo) {
        if (CollectionUtils.isNotEmpty(monthColdInfo)) {
            //取最大时间
            List<SlStationMes3Detail> glassData = getColdMaxTime(monthColdInfo);
            //统计
            int ztLp = 0;//直投良品数
            int tr = 0;//投入数
            int bzcpLp = 0;//包装成品良品数
            for (SlStationMes3Detail detail : glassData) {
                //直投良品数
                ztLp += detail.getZtLpNum();
                //投入数
                tr += (detail.getZtLpNum() + detail.getNgNum()
                        + detail.getPort1ZjgNum() + detail.getPort2ZjgNum() + detail.getPort3ZjgNum()
                        + detail.getPort1LpNum() + detail.getPort2LpNum() + detail.getPort3LpNum()
                        + detail.getPort1ZqxNum() + detail.getPort2ZqxNum() + detail.getPort3ZqxNum());
                //包装成品良品数
                bzcpLp = detail.getPort2CfNum() + detail.getPort2TfNum() + detail.getPort2DpNum();
            }
            monthProductionInfo.setC13(String.valueOf(tr));
            monthProductionInfo.setC16(String.valueOf(ztLp));
            monthProductionInfo.setC19(String.valueOf(bzcpLp));
        }
    }

    private List<SlStationMes3Detail> getColdMaxTime(List<SlStationMes3Detail> monthColdInfo) {
        List<SlStationMes3Detail> glassData = new ArrayList<>();
        Map<String, List<SlStationMes3Detail>> slStationMes3ColdDetailMap = monthColdInfo.stream().filter(e -> StringUtils.isNotBlank(e.getCreateTime()))
                .collect(Collectors.groupingBy(e -> e.getCreateTime().substring(0, 13)));
        slStationMes3ColdDetailMap.forEach((k, v) -> {
            Optional<SlStationMes3Detail> max = v.stream().max(Comparator.comparing(SlStationMes3Detail::getCreateTime));
            if (max.isPresent()) {
                SlStationMes3Detail slStationMes3Detail = max.get();
                glassData.add(slStationMes3Detail);
//                log.error("冷端时间最大的记录是：{}", slStationMes3Detail.getCreateTime());
            } else {
                log.error("List为空，没有最大的记录");
            }
        });
        return glassData;
    }

    private SchedulingHotInfo processHotMes3(List<BcStationMesDetail> bcStationMesDetails) {
        if (CollectionUtils.isNotEmpty(bcStationMesDetails)) {
            //取小时中最大数据
            List<BcStationMesDetail> glassHotData = getHotMaxTime(bcStationMesDetails);
            //统计
            if (CollectionUtils.isNotEmpty(glassHotData)) {
                int dhl = 0;//多缺角、划伤及裂纹
                int bb = 0;//掰板损失
                int sb = 0;//上板损失
                int zq = 0;//纵切损失
                int bcpSb = 0;//半成品收包数
                int bcpZt = 0;//半成品直投数
                int xsz = 0; //线体、收包、直投损失
                int llHq = 0;//理论横切数
                int sjHq = 0;//实际横切数
                int tb = 0;//人工推板数
                for (BcStationMesDetail detail : glassHotData) {
                    //线体、收包、直投损失
                    xsz += (detail.getFxxtDiscardNum() + detail.getFxjqrDiscardNum() + detail.getXbxtDiscardNum() + detail.getXbjqrDiscardNum() + detail.getXtReceiveNum());
                    //多缺角、划伤及裂纹
                    dhl += (detail.getReasonDqj() + detail.getReasonHs() + detail.getReasonLw());
                    //掰板损失
                    bb += detail.getBbNum();
                    //上板损失
                    sb += detail.getSbDiscard();
                    //纵切损失
                    zq += detail.getZqDiscard();
                    //半成品收包数
                    bcpSb += (detail.getFxRack() + detail.getXbRack());
                    //半成品直投数
                    bcpZt += detail.getXbLink();
                    //理论横切数
                    llHq += detail.getHqExpectCount();
                    //实际横切数
                    sjHq += detail.getRealityHq();
                    //人工推板数
                    tb += detail.getTbNum();
                }
                //功耗
                int gh = bb + tb + sb + zq + dhl + xsz;
                SchedulingHotInfo schedulingHotInfo = new SchedulingHotInfo();
                schedulingHotInfo.setA25(String.valueOf(dhl));
                schedulingHotInfo.setA13(String.valueOf(bb));
                schedulingHotInfo.setA19(String.valueOf(sb));
                schedulingHotInfo.setA22(String.valueOf(zq));
                schedulingHotInfo.setA10(String.valueOf(bcpSb));
                schedulingHotInfo.setA7(String.valueOf(bcpZt));
                schedulingHotInfo.setA46(String.valueOf(xsz));
                schedulingHotInfo.setA1(String.valueOf(llHq));
                schedulingHotInfo.setA4(String.valueOf(sjHq));
                schedulingHotInfo.setA16(String.valueOf(tb));
                schedulingHotInfo.setA44(String.valueOf(gh));
                return schedulingHotInfo;
            }
        }
        return null;
    }


    private SchedulingColdInfo processColdMes3(List<SlStationMes3Detail> slStationMes3ColdDetails) {
        if (CollectionUtils.isNotEmpty(slStationMes3ColdDetails)) {
            //取小时中最大数据
            List<SlStationMes3Detail> glassData = getColdMaxTime(slStationMes3ColdDetails);
            //统计
            if (CollectionUtils.isNotEmpty(glassData)) {
                int cj = 0;//抽检
                int ztLp = 0;//直投良品
                int ng = 0;//直投NG
                int aLp = 0;//A型架良品数
                int zqx = 0;//再清洗反投数
                int sp = 0;//碎片
                int cpFt = 0;//成品返投
                for (SlStationMes3Detail detail : glassData) {
                    //抽检
                    cj += (detail.getCjNum() + detail.getBzCjNum());
                    //直投良品
                    ztLp += detail.getZtLpNum();
                    //直投NG
                    ng += detail.getNgNum();
                    //A型架良品数
                    aLp += (detail.getPort1LpNum() + detail.getPort2LpNum() + detail.getPort3LpNum());
                    //再清洗反投数
                    zqx += (detail.getPort1ZqxNum() + detail.getPort2ZqxNum() + detail.getPort3ZqxNum());
                    //碎片
                    sp += (detail.getLink1SblNum() + detail.getLink2SblNum() + detail.getBzSbNum());
                    //成品返投
                    cpFt += (detail.getPort1ZjgNum() + detail.getPort2ZjgNum() + detail.getPort3ZjgNum());
                }
                //投入数
                int trNum = ztLp + ng + aLp + zqx + cpFt;
                SchedulingColdInfo schedulingColdInfo = new SchedulingColdInfo();
                schedulingColdInfo.setB4(String.valueOf(aLp));
                schedulingColdInfo.setB78(String.valueOf(sp));
                schedulingColdInfo.setB75(String.valueOf(zqx));
                schedulingColdInfo.setB77(String.valueOf(cj));
                schedulingColdInfo.setB73(String.valueOf(ng));
                schedulingColdInfo.setB1(String.valueOf(ztLp));
                schedulingColdInfo.setB16(String.valueOf(cpFt));
                schedulingColdInfo.setB67(String.valueOf(trNum));
                return schedulingColdInfo;
            }
        }
        return null;
    }
}
